package com.ruoyi.business.service.s7;

import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CompletableFuture;

/**
 * S7协议客户端
 * 基于iot-communication库实现
 *
 * @author qiancheng
 * @date 2025-09-02
 */
public class S7Client {
    private static final Logger log = LoggerFactory.getLogger(S7Client.class);

    private final String host;
    private final int port;
    private final int rack;
    private final int slot;
    
    private S7PLC s7PLC;
    private volatile boolean connected = false;
    private long lastActivityTime = 0;

    public S7Client(String host, int port, int rack, int slot) {
        this.host = host;
        this.port = port;
        this.rack = rack;
        this.slot = slot;
    }

    /**
     * 建立连接
     */
    public CompletableFuture<Boolean> connect() {
        return CompletableFuture.supplyAsync(() -> {
            try {
                if (connected && s7PLC != null) {
                    return true;
                }
                
                s7PLC = new S7PLC(EPlcType.S1500, host, port, rack, slot);
                s7PLC.connect();
                connected = true;
                lastActivityTime = System.currentTimeMillis();
                log.info("S7连接成功: {}:{} rack:{} slot:{}", host, port, rack, slot);
                return true;
            } catch (Exception e) {
                connected = false;
                log.error("S7连接失败: {}:{} rack:{} slot:{}", host, port, rack, slot, e);
                return false;
            }
        });
    }

    /**
     * 读取DB块中的浮点数数据
     */
    public CompletableFuture<Float> readFloat32(String address) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                checkConnection();
                Float value = s7PLC.readFloat32(address);
                lastActivityTime = System.currentTimeMillis();
                return value;
            } catch (Exception e) {
                log.error("读取浮点数数据失败: {}", address, e);
                throw new RuntimeException("读取浮点数数据失败: " + e.getMessage(), e);
            }
        }).exceptionally(throwable -> {
            handleReadException(throwable, "浮点数");
            return 0.0f;
        });
    }

    /**
     * 读取DB块中的整数数据
     */
    public CompletableFuture<Integer> readInt16(String address) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                checkConnection();
                Short shortValue = s7PLC.readInt16(address);
                Integer value = shortValue != null ? shortValue.intValue() : 0;
                lastActivityTime = System.currentTimeMillis();
                return value;
            } catch (Exception e) {
                log.error("读取整数数据失败: {}", address, e);
                throw new RuntimeException("读取整数数据失败: " + e.getMessage(), e);
            }
        }).exceptionally(throwable -> {
            handleReadException(throwable, "整数");
            return 0;
        });
    }

    /**
     * 读取DB块中的双精度浮点数数据
     */
    public CompletableFuture<Double> readFloat64(String address) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                checkConnection();
                Double value = s7PLC.readFloat64(address);
                lastActivityTime = System.currentTimeMillis();
                return value;
            } catch (Exception e) {
                log.error("读取双精度浮点数数据失败: {}", address, e);
                throw new RuntimeException("读取双精度浮点数数据失败: " + e.getMessage(), e);
            }
        }).exceptionally(throwable -> {
            handleReadException(throwable, "双精度浮点数");
            return 0.0;
        });
    }

    /**
     * 读取DB块中的字节数据
     */
    public CompletableFuture<byte[]> readByte(String address, int count) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                checkConnection();
                byte[] value = s7PLC.readByte(address, count);
                lastActivityTime = System.currentTimeMillis();
                return value;
            } catch (Exception e) {
                log.error("读取字节数据失败: {}", address, e);
                throw new RuntimeException("读取字节数据失败: " + e.getMessage(), e);
            }
        }).exceptionally(throwable -> {
            handleReadException(throwable, "字节");
            return new byte[0];
        });
    }

    /**
     * 检查连接状态
     */
    private void checkConnection() throws IllegalStateException {
        if (!connected || s7PLC == null) {
            throw new IllegalStateException("S7连接未建立");
        }
        
        // 检查连接是否仍然有效
        try {
            if (!s7PLC.checkConnected()) {
                connected = false;
                throw new IllegalStateException("S7连接已断开");
            }
        } catch (Exception e) {
            connected = false;
            throw new IllegalStateException("S7连接状态检查失败: " + e.getMessage(), e);
        }
    }

    /**
     * 处理读取异常
     */
    private void handleReadException(Throwable throwable, String dataType) {
        if (throwable.getCause() instanceof IllegalStateException) {
            connected = false;
            log.error("读取{}数据时连接异常: {}", dataType, throwable.getCause().getMessage());
        } else {
            log.error("读取{}数据时发生异常", dataType, throwable);
        }
    }

    /**
     * 检查连接状态
     */
    public boolean isConnected() {
        if (connected && s7PLC != null) {
            try {
                return s7PLC.checkConnected();
            } catch (Exception e) {
                connected = false;
                return false;
            }
        }
        return false;
    }

    /**
     * 获取最后活动时间
     */
    public long getLastActivityTime() {
        return lastActivityTime;
    }

    /**
     * 断开连接
     */
    public void disconnect() {
        connected = false;
        if (s7PLC != null) {
            try {
                s7PLC.close();
                log.info("S7连接已关闭: {}:{}", host, port);
            } catch (Exception e) {
                log.warn("关闭S7连接时发生异常: {}:{}", host, port, e);
            }
            s7PLC = null;
        }
    }

    /**
     * 读取DB块中的布尔数据
     */
    /**
     * 读取DB块中的布尔数据（使用info级别日志，解决DB97.DBX4.7读取问题）
     */
    public CompletableFuture<Boolean> readBool(String address) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                checkConnection();
                log.info("开始读取地址: {}", address);

                // 解析地址各部分
                int dbNumber = parseDbNumber(address);
                int byteOffset = parseByteOffset(address);
                int bitOffset = parseBitOffset(address);

                log.info("地址解析结果 - DB号: {}, 字节偏移: {}, 位偏移: {}",
                        dbNumber, byteOffset, bitOffset);

                // 验证位偏移有效性
                if (bitOffset < 0 || bitOffset > 7) {
                    String error = "位偏移必须在0-7之间，当前值: " + bitOffset;
                    log.error(error);
                    throw new IllegalArgumentException(error);
                }

                // 读取对应字节
                String byteAddress = String.format("DB%d.DBB%d", dbNumber, byteOffset);
                log.info("读取字节地址: {}", byteAddress);

                byte[] bytes = s7PLC.readByte(byteAddress, 1);
                if (bytes == null || bytes.length == 0) {
                    log.error("读取字节返回空值，地址: {}", byteAddress);
                    return false;
                }

                // 输出原始字节值（关键排查信息）
                int byteValue = bytes[0] & 0xFF; // 转换为无符号整数
                log.info("原始字节值 - 十进制: {}, 十六进制: 0x{}, 二进制: {}",
                        byteValue,
                        Integer.toHexString(byteValue).toUpperCase(),
                        String.format("%8s", Integer.toBinaryString(byteValue)).replace(' ', '0'));

                // 位运算逻辑
                boolean value = (bytes[0] & (1 << bitOffset)) != 0;
                log.info("位运算详情 - 位移值: 1 << {} = {}, 与运算结果: {}, 最终值: {}",
                        bitOffset,
                        (1 << bitOffset),
                        (bytes[0] & (1 << bitOffset)),
                        value);

                lastActivityTime = System.currentTimeMillis();
                return value;
            } catch (Exception e) {
                log.error("读取布尔数据失败: {}", address, e);
                throw new RuntimeException("读取布尔数据失败: " + e.getMessage(), e);
            }
        }).exceptionally(throwable -> {
            handleReadException(throwable, "布尔");
            return false;
        });
    }

    // 辅助方法：解析DB号
    private int parseDbNumber(String address) {
        try {
            String dbPart = address.split("\\.")[0];
            return Integer.parseInt(dbPart.substring(2));
        } catch (Exception e) {
            throw new IllegalArgumentException("地址格式错误，无法解析DB号: " + address, e);
        }
    }

    // 辅助方法：解析字节偏移
    private int parseByteOffset(String address) {
        try {
            String dbxPart = address.split("\\.")[1];
            return Integer.parseInt(dbxPart.substring(3));
        } catch (Exception e) {
            throw new IllegalArgumentException("地址格式错误，无法解析字节偏移: " + address, e);
        }
    }

    // 辅助方法：解析位偏移
    private int parseBitOffset(String address) {
        try {
            return Integer.parseInt(address.split("\\.")[2]);
        } catch (Exception e) {
            throw new IllegalArgumentException("地址格式错误，无法解析位偏移: " + address, e);
        }
    }


    /**
     * 从地址解析 bit 偏移
     * e.g. "DB1.DBX0.2" -> 2
     */
//    private int parseBitOffset(String address) {
//        if (address.contains(".")) {
//            String[] parts = address.split("\\.");
//            return Integer.parseInt(parts[parts.length - 1]);
//        }
//        return 0;
//    }


}